package httpflv

import (
	"errors"
	"fmt"
	"github.com/google/uuid"
	"net/http"
	"time"
	"xmediaEmu/pkg/decoder/codec"
	"xmediaEmu/pkg/format/flv/flvio"
	"xmediaEmu/pkg/log"
	"xmediaEmu/pkg/util/bits/pio"
)

const (
	headerLen   = 11
	maxQueueNum = 1024
)

type FLVWriter struct {
	Uid string
	codec.RWBaser
	app, title, url string
	buf             []byte
	closed          bool
	closedChan      chan struct{}
	ctx             http.ResponseWriter
	packetQueue     chan *codec.Packet

	// 根据stream id区分audio还是video.
	streams []codec.CodecData
}

func NewFLVWriter(app, title, url string, ctx http.ResponseWriter) (*FLVWriter, error) {
	ret := &FLVWriter{
		Uid:         uuid.NewString(),
		app:         app,
		title:       title,
		url:         url,
		ctx:         ctx,
		RWBaser:     codec.NewRWBaser(time.Second * 10),
		closedChan:  make(chan struct{}),
		buf:         make([]byte, headerLen),
		packetQueue: make(chan *codec.Packet, maxQueueNum),
	}

	if _, err := ret.ctx.Write([]byte{0x46, 0x4c, 0x56, 0x01, 0x05, 0x00, 0x00, 0x00, 0x09}); err != nil {
		ret.closed = true
		return ret, errors.New("Error on response writer ")
	}
	pio.PutI32BE(ret.buf[:4], 0)
	if _, err := ret.ctx.Write(ret.buf[:4]); err != nil {
		ret.closed = true
		return ret, errors.New("Error on response writer after PutI32BE ")
	}
	go func() {
		// 不停的发送rtp流来的packet.
		err := ret.SendPacket()
		if err != nil {
			log.Logger.Debug("SendPacket error: ", err)
			ret.closed = true
		}

	}()
	return ret, nil
}

// WriteHeader 用来初始化rtp流的codecdata数据；rtmp过来的数据codecdata可以直接复制过来.
func (flvWriter *FLVWriter) SetCodecData(streams []codec.CodecData) (err error) {
	// 直接设置不做其他处理.
	// todo: 怎么确定文件头信息.
	flvWriter.streams = streams
	return
}

// DropPacket Packet根据codec data转为是否音视频
func (flvWriter *FLVWriter) DropPacket(pktQue chan *codec.Packet) {
	log.Logger.Debug("packet queue max!!!")
	for i := 0; i < maxQueueNum-84; i++ {
		tmpPkt, ok := <-pktQue
		codecType := codec.CodecType(tmpPkt.Idx)

		// 只有视频才有关键帧, 目前视频只支持H264，其他所有的都当做音频处理.
		if ok && codecType == codec.H264 {
			// dont't drop sps config and dont't drop key frame
			if ok && tmpPkt.IsKeyFrame {
				log.Logger.Debug("insert keyframe to queue")
				pktQue <- tmpPkt
			}

			if len(pktQue) > maxQueueNum-10 {
				<-pktQue
			}
			// drop other packet
			<-pktQue
		} else if ok { // try to don't drop audio
			log.Logger.Debug("insert audio to queue")
			pktQue <- tmpPkt
		}
	}
	log.Logger.Debug("packet queue len: ", len(pktQue))
}

func (flvWriter *FLVWriter) Write(p *codec.Packet) (err error) {
	err = nil
	if flvWriter.closed {
		err = fmt.Errorf("flvwrite source closed")
		return
	}

	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("FLVWriter has already been closed:%v", e)
		}
	}()

	if len(flvWriter.packetQueue) >= maxQueueNum-24 {
		flvWriter.DropPacket(flvWriter.packetQueue)
	} else {
		flvWriter.packetQueue <- p
	}

	return
}

func (flvWriter *FLVWriter) SendPacket() error {
	for {
		p, ok := <-flvWriter.packetQueue
		if ok {
			flvWriter.RWBaser.SetPreTime()
			h := flvWriter.buf[:headerLen]
			codecType := codec.CodecType(p.Idx)
			typeID := flvio.TAG_VIDEO
			if codecType != codec.H264 {
				// TAG_SCRIPTDATA问题,
				//if p.TAG_SCRIPTDATA { // TAG_SCRIPTDATA
				//	var err error
				//	typeID = flvio.TAG_SCRIPTDATA
				//	p.Data, err = amf.MetaDataReform(p.Data, amf.DEL)
				//	if err != nil {
				//		return err
				//	}
				//} else {
				typeID = flvio.TAG_AUDIO
				// }
			}
			dataLen := len(p.Data)
			timestamp := uint32(p.Time.Seconds())
			timestamp += flvWriter.BaseTimeStamp()
			flvWriter.RWBaser.RecTimeStamp(timestamp, codecType)

			preDataLen := dataLen + headerLen
			timestampbase := timestamp & 0xffffff
			timestampExt := timestamp >> 24 & 0xff

			pio.PutU8(h[0:1], uint8(typeID))
			pio.PutI24BE(h[1:4], int32(dataLen))
			pio.PutI24BE(h[4:7], int32(timestampbase))
			pio.PutU8(h[7:8], uint8(timestampExt))

			if _, err := flvWriter.ctx.Write(h); err != nil {
				return err
			}

			if _, err := flvWriter.ctx.Write(p.Data); err != nil {
				return err
			}

			pio.PutI32BE(h[:4], int32(preDataLen))
			if _, err := flvWriter.ctx.Write(h[:4]); err != nil {
				return err
			}
		} else {
			return fmt.Errorf("closed")
		}

	}
}

func (flvWriter *FLVWriter) Wait() {
	select {
	case <-flvWriter.closedChan:
		return
	}
}

func (flvWriter *FLVWriter) Close(error) {
	log.Logger.Debug("http flv closed")
	if !flvWriter.closed {
		close(flvWriter.packetQueue)
		close(flvWriter.closedChan)
	}
	flvWriter.closed = true
}
